/*
 * Copyright (c) 2010, University of Innsbruck, Austria.
 *
 * This library is free software; you can redistribute it and/or modify it under
 * the terms of the GNU Lesser General Public License as published by the Free
 * Software Foundation; either version 2.1 of the License, or (at your option)
 * any later version.
 * This library is distributed in the hope that it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
 * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
 * details.
 * You should have received a copy of the GNU Lesser General Public License along
 * with this library; if not, write to the Free Software Foundation, Inc.,
 * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
 */

package at.sti2.sepn.pattern.network.beta;

import java.util.ArrayList;
import java.util.List;
import java.util.Vector;

import at.sti2.sepn.pattern.network.WorkingMemoryElement;
import at.sti2.sepn.pattern.network.alpha.AlphaMemory;
import at.sti2.sepn.pattern.network.node.RETENode;
import at.sti2.sepn.pattern.network.node.Token;

/**
 * Negative node data structure
 * 
 * @author skomazec
 *
 */

public class NegativeNode extends RETENode {
	
	private List <Token> items = null;
	private AlphaMemory alphaMemory = null;
	private List <JoinNodeTest> tests = null;

	public NegativeNode(){
		items = new ArrayList <Token> ();
		tests = new ArrayList <JoinNodeTest> ();
	}
	
	public void addItem(Token token){
		items.add(token);
	}
	
	public List <Token> getItems(){
		return items;
	}
	
	public AlphaMemory getAlphaMemory() {
		return alphaMemory;
	}

	public void setAlphaMemory(AlphaMemory alphaMemory) {
		this.alphaMemory = alphaMemory;
	}
	
	public List<JoinNodeTest> getTests() {
		return tests;
	}
	
	public void setTests(List<JoinNodeTest> tests) {
		this.tests = tests;
	}

	public void addJoinNodeTest(JoinNodeTest test){
		tests.add(test);
	}
	
	/**
	 * Builds and stores a new token
	 * 
	 * @param token
	 * @param wme
	 */
	public void leftActivate(Token token, WorkingMemoryElement wme) {
		
		Token newToken = createToken(token, wme);
		items.add(newToken);
		
		for (WorkingMemoryElement alphaWME : alphaMemory.getItems())
			if (performTests(newToken, alphaWME)){
				NegativeJoinResult njr = new NegativeJoinResult();
				njr.setOwner(newToken);
				njr.setWme(wme);
				newToken.addNegativeJoinResult(njr);
				wme.addNegativeJoinResult(njr);
			}
		
		if (newToken.getNegativeJoinResults().isEmpty())
			for (RETENode reteNode : children)
				reteNode.leftActivate(newToken);

	}

	@Override
	public void rightActivate(WorkingMemoryElement wme) {
		
		for (Token negNodeToken : items)
			if (performTests(negNodeToken, wme)){
				if (negNodeToken.getNegativeJoinResults().isEmpty())
					deleteDescendentsOfToken(negNodeToken);
				
				NegativeJoinResult njr = new NegativeJoinResult();
				njr.setOwner(negNodeToken);
				njr.setWme(wme);
				
				negNodeToken.addNegativeJoinResult(njr);
				wme.addNegativeJoinResult(njr);
			}

	}
	
	private void deleteDescendentsOfToken(Token token){
		
		for (Token childToken : token.getChildren())
			deleteDescendentsOfToken(childToken);
		
	}
	
	public void deleteTokenAndDescendents(Token token){
		
		for (Token childToken : token.getChildren())
			deleteTokenAndDescendents(childToken);
		
		//Delete token from the list of items in BETA, Negative and Production nodes
		//TODO CREATE MEMORY NODE WHICH WILL HOLD ITEMS
		if (token.getNode() instanceof BetaMemory)
			((BetaMemory)token.getNode()).getItems().remove(token);
		else if (token.getNode() instanceof ProductionNode)
			((ProductionNode)token.getNode()).getItems().remove(token);
		else if (token.getNode() instanceof NegativeNode)
			((NegativeNode)token.getNode()).getItems().remove(token);
		
		//Remove token from the WME's token list
		if (token.getWme() != null)
			token.getWme().getTokens().remove(token);
		
		//Remove token token's parent children
		token.getParent().getChildren().remove(token);
		
	}
	
	public boolean performTests (Token token, WorkingMemoryElement wme){
		
		String lexicalValueArg1 = null;
		String lexicalvalueArg2 = null;
		
		for (JoinNodeTest test : tests){
			lexicalValueArg1 = wme.getTriple().getLexicalValueOfField(test.getArg1Field());
			
			//Select WME
			int index = test.getArg2ConditionNumber();
			
			//TODO Fix this for faster processing; instead of using indices maybe we can use pointers?!
			//The value I have is the one of index in the tree, while the only thing I have is the token from which to start.
			
			Vector<Token> wmeTokenVect = getTokenVect(token);
			Token wmeToken = wmeTokenVect.get(index);
			
			lexicalvalueArg2 = wmeToken.getWme().getTriple().getLexicalValueOfField(test.getArg2Field());
			
			if (!lexicalValueArg1.equals(lexicalvalueArg2))
				return false;
		}
		
		return true;
	}
	
	private Vector<Token> getTokenVect(Token token){
		
		Vector <Token> tokenVect = new Vector <Token>();
		
		Token tempToken = token;
		
		while (tempToken != null){
			tokenVect.add(0, tempToken);
			tempToken = tempToken.getParent();
		}
			
		return tokenVect;
		
	}
	
	private Token createToken(Token parentToken, WorkingMemoryElement wme){
		
		Token token = new Token();
		
		//Set parent
		token.setParent(parentToken);
		
		//Set WME
		token.setWme(wme);
		
		//Set node for tree based removal
		token.setNode(this);
		
		//TODO Insert token at the head of parent's children
		if (parentToken!=null)
			parentToken.addChild(token);
		
		//if WME is != null add it to the wme.tokens
		if (wme != null)
			wme.addToken(token);
		
		return token;
	}

	@Override
	public void leftActivate(Token token) {
		// TODO Auto-generated method stub
		
	}

}
